package cn.tedu.justone.pack02;

/**
 * ZZ：JustOne
 * VX：Andylau2020smile
 * DY：JustOne2018
 * ZT：《》
 */
public class Demo10_4_案例测试synchronized的嵌套和死锁 {

    //案例测试synchronized的嵌套使用
    //案例测试synchronized的嵌套使用
    //案例测试synchronized的嵌套使用

    //测试1:
    // 线程t1在锁lockA的保护下使用试衣间room()
    // 线程t2在锁lockB的保护下使用洗手间wc()
    // 各做各的，互不影响。
    //
    //测试2:
    // 线程t1在锁lockA的保护下使用试衣间room() 和 洗手间wc()，
    // 线程t2在锁lockB的保护下使用洗手间wc()，
    // 由于wc()受不同的锁对象保持，没有互斥效果，会出现同时使用wc()的情况，
    // 存在线程安全问题。
    //
    //测试3:
    // 线程t1在锁lockA的保护下使用试衣间room()时，又用lockB锁定了洗手间wc(),
    // 线程t2在锁lockB的保护下使用洗手间wc(),
    // 由于t1、t2双方都认可lockB锁对象，所以在wc()资源上有互斥效果。线程安全。
    //
    //测试4:    ---死锁
    // 线程t1在锁lockA的保护下使用试衣间room()时，又用lockB锁定了洗手间wc(),
    // 线程t2在锁lockB的保护下使用洗手间wc()时，又用lockA锁定了试衣间room(),
    // 启动后，t1持有着lockA等着t2释放lockB，t2持有着lockB等着t1释放lockA，
    // 程序没有终止，但无法继续，形成死锁。
    // 修改：让线程获取锁的顺序相同，例如都是从lockA到lockB，就不会出现死锁了。
    //

    //死锁的产生有四个必要的条件
    // 1,互斥使用，即当资源被一个线程占用时，别的线程不能使用
    // 2,不可抢占，资源请求者不能强制从资源占有者手中抢夺资源，只能由占有者主动释放
    // 3,请求和保持，当资源请求者在请求其他资源的同时保持对原有资源的占有
    // 4,循环等待，多个线程存在环路的锁依赖关系而永远等待下去
    // 对于死锁产生的四个条件只要能破坏其中一条即可让死锁消失，但是条件一是基础，不能被破坏。

    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";

        Runnable r1 = ()->{
            synchronized (lockA) {
                room();
                synchronized (lockB){
                    wc();
                }
            }
        };
        Runnable r2 = ()->{
            synchronized (lockB) {
                wc();
                synchronized (lockA){
                    room();
                }
            }
        };

        Thread t1 = new Thread(r1,"t1");
        Thread t2 = new Thread(r2,"t2");
        t1.start();
        t2.start();
    }

    public static void room(){
        System.out.println(Thread.currentThread().getName() + " => room begin");
        mySleep(1000);
        System.out.println(Thread.currentThread().getName() + " => roon end");
    }

    public static void wc(){
        System.out.println(Thread.currentThread().getName() + " => wc begin");
        mySleep(1000);
        System.out.println(Thread.currentThread().getName() + " => wc end");
    }

    //为了让代码更优雅，编写统一的休眠方法
    public static void mySleep(long m){
        try {
            Thread.sleep(m);
        } catch (InterruptedException e) {
            System.out.println("You：谁喊我？");
            e.printStackTrace();
        }
    }

}
